package i5.modelman.chase;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import jpl.Compound;
import jpl.Term;
import jpl.Util;

/**
 * Tableau is a data structure representing a database, with variables allowed.
 * Note, a variable in a tableau is always represented by a functional term. It
 * is NOT a prolog variable.
 * 
 * @author lixiang
 * 
 */
public class Tableau implements Iterable<Compound> {
	private List<Compound> contents = new LinkedList<Compound>();

	public Iterator<Compound> getCompounds() {
		return contents.iterator();
	}

	void loadFromCompounds(Iterator<Compound> iter) {
		while (iter.hasNext()) {
			addCompound(iter.next());
		}
	}

	public void addCompound(Compound c) {
		contents.add(c);
	}
	
	public void clear(){
		contents.clear();
	}

	public void serializeToFile(File file) throws IOException {
		FileWriter writer = new FileWriter(file);
		Iterator<String> it = getTableauAsStrings();
		while (it.hasNext()) {
			writer.write(it.next() + "\r\n");
		}
	}

	public void loadFromFile(File file) throws IOException {
		FileReader fileReader = new FileReader(file);
		BufferedReader bufReader = new BufferedReader(fileReader);
		String line;
		while ((line = bufReader.readLine()) != null) {
			if(line.length() > 0){
				loadCompoundFromString(line.trim());
			}
		}

	}

	public void loadCompoundFromString(String text) {
		Term t = Util.textToTerm(text);
		loadTerm(t);
	}

	private void loadTerm(Term t) {
		if (t instanceof Compound) {
			addCompound((Compound) t);
		} else {
			throw new RuntimeException("Expected a compound, encountered: "
					+ t.getClass());
		}
	}

	void loadFromStrings(Iterator<String> stringIt) {
		while (stringIt.hasNext()) {
			loadCompoundFromString(stringIt.next());
		}
	}

	public Iterator<String> getTableauAsStrings() {

		return new Iterator<String>() {
			private Iterator<Compound> compoundIter = getCompounds();

			public boolean hasNext() {
				return compoundIter.hasNext();
			}

			public String next() {
				Compound c = compoundIter.next();
				return c.toString();
			}

			public void remove() {
				compoundIter.remove();
			}

		};
	}

	public void loadFromProlog(/* String moduleName */) {
		Iterator<Term> it = PrologUtil.factIterator();
		while (it.hasNext()) {
			loadTerm(it.next());
		}
	}

	public void tellTableauToProlog() {
		Iterator<Compound> compoundIt = getCompounds();
		while (compoundIt.hasNext()) {
			PrologUtil.addFact(compoundIt.next());
		}
	}

	public boolean contains(Tableau t) {
		PrologUtil.clearFacts();
		tellTableauToProlog();
		Iterator<String> it = t.getTableauAsStrings();
		
		boolean contain = true;
		while(it.hasNext()){
			if(!PrologUtil.isFact(it.next())){
				contain = false;
				break;
			}
		}
		
		PrologUtil.clearFacts();
		return contain;
	}
	
	public boolean containsFact(Compound fact){
		for(Compound c : contents){
			if(c.equals(fact)){
				return true;
			}
		}
		return false;
	}
	
	public int size(){
		return contents.size();
	}

	public void addTableau(Tableau another) {
		for(Compound c : another){
			this.addCompound(c);
		}
	}

	@Override
	public Iterator<Compound> iterator() {
		return getCompounds();
	}
}
